package com.iteaj.util.json.jackson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ContainerNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iteaj.util.CommonUtils;
import com.iteaj.util.core.UtilsException;
import com.iteaj.util.core.UtilsType;
import com.iteaj.util.json.AbstractJson;
import com.iteaj.util.json.Json;
import com.iteaj.util.json.JsonArray;
import com.iteaj.util.json.Type;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Date;
import java.util.Optional;

/**
 * create time: 2018/3/29
 *  jackson 适配
 * @author iteaj
 * @version 1.0
 * @since 1.7
 */
public class Jackson extends AbstractJson<ContainerNode> {

    private ContainerNode node;
    private ObjectMapper mapper;

    public Jackson(ContainerNode node, ObjectMapper mapper) {
        this.node = node;
        this.mapper = mapper;
    }

    @Override
    public Type getType() {
        return Type.Jackson;
    }

    @Override
    public ContainerNode original() {
        return this.node;
    }

    @Override
    public int size() {
        return original().size();
    }

    @Override
    public Json append(String name, Object val) {
        if(CommonUtils.isBlank(name))
            throw new UtilsException("新增Json节点时必须指定节点名", UtilsType.JSON);

        if(original() instanceof ObjectNode) {
            if(val instanceof Jackson) {
                ((ObjectNode) original()).set(name, ((Jackson) val).original());
            } else if(val instanceof JacksonArray) {
                ((ObjectNode) original()).set(name, ((JacksonArray) val).original());
            } else {
                ((ObjectNode) original()).putPOJO(name, val);
            }
        } else {
            throw new UtilsException("只允许ObjectNode实例新增节点", UtilsType.JSON);
        }

        return this;
    }

    @Override
    public Json remove(String name) {
        if(original() instanceof ObjectNode) {
            ((ObjectNode) original()).remove(name);
        }

        return this;
    }

    @Override
    public boolean isExist(String name) {
        JsonNode jsonNode = original();
        return jsonNode !=null && jsonNode.has(name);
    }

    @Override
    public boolean isNull(String name) {
        JsonNode jsonNode = original().get(name);

        return isExist(name) || jsonNode.isNull();
    }

    @Override
    public boolean booleanValue(String name) {
        return getBoolean(name).orElse(false);
    }

    @Override
    public String toJsonString() {
        try {
            return this.mapper.writeValueAsString(this.original());
        } catch (JsonProcessingException e) {
            throw new UtilsException("写出Json字符串失败", e, UtilsType.JSON);
        }
    }

    @Override
    public Optional<Boolean> getBoolean(String name) {
        JsonNode node = this.original().get(name);
        return Optional.ofNullable(node).map(val -> val.asBoolean());
    }

    @Override
    public short shortValue(String name) {
        return getShort(name).orElse((short) 0);
    }

    @Override
    public Optional<Short> getShort(String name) {
        return Optional.ofNullable(original().get(name)).map(val -> val.shortValue());
    }

    @Override
    public int intValue(String name) {
        return getInt(name).orElse(0);
    }

    @Override
    public Optional<Integer> getInt(String name) {
        return Optional.ofNullable(original().get(name)).map(val -> val.intValue());
    }

    @Override
    public long longValue(String name) {
        return getLong(name).orElse(0l);
    }

    @Override
    public Optional<Long> getLong(String name) {
        return Optional.ofNullable(original().get(name)).map(val -> val.longValue());
    }

    @Override
    public float floatValue(String name) {
        return getFloat(name).orElse(0.0f);
    }

    @Override
    public Optional<Float> getFloat(String name) {
        return Optional.ofNullable(original().get(name)).map(val -> val.floatValue());
    }

    @Override
    public double doubleValue(String name) {
        return getDouble(name).orElse(0.00);
    }

    @Override
    public Optional<Double> getDouble(String name) {
        return Optional.ofNullable(original().get(name)).map(val -> val.doubleValue());
    }

    @Override
    public Optional<BigDecimal> getBigDecimal(String name) {
        return Optional.ofNullable(original().get(name)).map(val -> val.decimalValue());
    }

    @Override
    public Optional<Date> getDate(String name) {
        return Optional.ofNullable(original().get(name)).map(val ->{
            try {
                return mapper.getDateFormat().parse(val.asText());
            } catch (ParseException e) {
                throw new UtilsException(e, UtilsType.JSON);
            }
        });
    }

    @Override
    public <E extends Enum> E enumValue(String name, Class<E> clazz) {
        return getEnum(name, clazz).orElse(null);
    }

    @Override
    public <E extends Enum> Optional<E> getEnum(String name, Class<E> clazz) {
        return getString(name).map(val -> (E) Enum.valueOf(clazz, val));
    }

    @Override
    public String stringValue(String name) {
        return getString(name).orElse(null);
    }

    @Override
    public Optional<String> getString(String name) {
        return Optional.ofNullable(original().get(name)).map(val -> val.textValue());
    }

    @Override
    public <R> R toBean(Class<R> clazz) {
        try {
            return mapper.treeToValue(original(), clazz);
        } catch (JsonProcessingException e) {
            throw new UtilsException("Json转对象失败："+original().toString()
                    +" --> "+clazz.getName(), e, UtilsType.JSON);
        }
    }

    @Override
    public JsonArray toJsonArray() {

        return new JacksonArray(mapper.getNodeFactory().arrayNode(), mapper);
    }

    @Override
    public Optional<JsonArray> isJsonArray(String name) {
        JsonNode node = original().get(name);
        return Optional.ofNullable(node)
                .filter(item -> item.isArray())
                .map(val -> new JacksonArray((ArrayNode) val, mapper));
    }

    @Override
    public Optional<Json> isJson(String name) {
        JsonNode node = this.original().get(name);
        return Optional.ofNullable(node)
                .filter(item -> item.isObject())
                .map(val -> new Jackson((ObjectNode) val, mapper));
    }

    @Override
    public <T> Optional<T> getPath(String path) {
        JsonNode node = this.original().at(path);

        if(node.isMissingNode()) return Optional.empty();
        else if(node.isNull()) return Optional.empty();
        else if(node.isNumber()) return Optional.of((T) node.numberValue());
        else if(node.isBoolean()) return Optional.of((T) Boolean.valueOf(node.booleanValue()));
        else if(node.isContainerNode()) {
            if(node instanceof ArrayNode) {
                return Optional.of((T) new JacksonArray((ArrayNode) node, mapper));
            } else {
                return Optional.of((T) new Jackson((ContainerNode) node, mapper));
            }
        }
        else return Optional.of((T) node.asText());
    }
}
